/*
 * Copyright (C) 2007,2008  Jonathan Hendler
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 * 
 * The authors can be contacted at: 
 * @author Jonathan Hendler [jonathan DOT hendler AT google DOT com]
 * 
 * @license GPLv2
 */

#ifndef TEST_H_
#define TEST_H_
/**
 * test suites
 * DB tests
 * UTF tests
 * LogicTests
 * 
 * Admin functions
 * 
 * */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <setjmp.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>    // sysconf(3)
//#include <windows.h>
#include "lib/cutest/CuTest.h"
#include "nymrl.h"

#ifdef DMALLOC
#include "dmalloc.h"
//see http://dmalloc.com/docs/latest/online/dmalloc_14.html for error codes
#endif


#define NYMRL_TEST_OUTPUT_STRING(msg,result) fprintf(stdout,"-->\t%s:\t%s\n", msg, (char *) result)
#define CompareAsserts(tc, message, expected, actual)  X_CompareAsserts((tc), __FILE__, __LINE__, (message), (expected), (actual))

//http://www.ncsu.edu/itd/hpc/Documents/timing.html
#define NYMRL_TIMER_START struct timeval start;\
	struct timeval end;\
	double wdiff; \
	int ierr = gettimeofday(&start, NULL); \
	if (ierr != 0 ) printf("bad return of gettime of day, ierr = %d \n", ierr); 

#define NYMRL_TIMER_END ierr = gettimeofday(&end, NULL);\
	if (ierr != 0 ) printf("bad return of gettime of day, ierr = %d \n", ierr);\
	double seconds = end.tv_sec - start.tv_sec;\
	double miliseconds = end.tv_usec - start.tv_usec;\
	wdiff = 1.e6*seconds + miliseconds;\
	double total_time =  ( wdiff/1.e6) ; 

char * myfunction(char * act, char * data);

//static void X_CompareAsserts(CuTest* tc, const char *file, int line, const char* message, const char* expected, const char* actual)
//{
//	int mismatch;
//	if (expected == NULL || actual == NULL) {
//		mismatch = (expected != NULL || actual != NULL);
//	} else {
//		const char *front = __FILE__ ":";
//		const size_t frontLen = strlen(front);
//		const size_t expectedLen = strlen(expected);
//
//		const char *matchStr = actual;
//
//		mismatch = (strncmp(matchStr, front, frontLen) != 0);
//		if (!mismatch) {
//			matchStr = strchr(matchStr + frontLen, ':');
//			mismatch |= (matchStr == NULL || strncmp(matchStr, ": ", 2));
//			if (!mismatch) {
//				matchStr += 2;
//				mismatch |= (strncmp(matchStr, expected, expectedLen) != 0);
//			}
//		}
//	}
//
//	CuAssert_Line(tc, file, line, message, !mismatch);
//}

void TestCuConfigValues(CuTest* tc)
{
	
	const char * config_key = "cutest_key";
	const char * config_key_fake = "h79qvby7q4crhiouq345ypv8cginoailnu";
	const char * config_value = "cutest_value";
	const char * config_update_value = "cutest_value_updated";
	static char * returned_value = NULL;
	int config_read_count = 18;
	int i =0;
	
	nyml_set_config(config_key, config_value);
	NYMRL_TIMER_START;
	
	for (i=0;i< config_read_count;++i){
		if (returned_value != NULL) free(returned_value);
		returned_value = nyml_get_config(config_key);
		printf(" '%s'?? ",returned_value);
		
	}
	 
	NYMRL_TIMER_END; 
	printf("\n\t-> read %d configs in %f seconds returned %s\n",config_read_count,total_time,returned_value);
	
	CuAssertTrue(tc, strlen(returned_value) > 0);
	
	if (strcmp(returned_value,config_value) != 0){
		printf("\n\t------->>>>>> Jono... ERROR: a=%s b=%s not equal <<<<<<\n\n",returned_value,config_value);
		CuAssertTrue(tc, 0==2 );
	}
	
	
	
	if (!nyml_assert_config(config_key,config_value)){
		printf("\n\t------->>>>>>  ERROR: %s \"%s\" should return true, instead \"%s\" <<<<<<\n\n",config_key,config_value,returned_value);
		CuAssertTrue(tc, 0==2 );
	}
	
	nyml_set_config(config_key, config_update_value);
	if (!nyml_assert_config(config_key,config_update_value)){
		if (returned_value != NULL) free(returned_value);
		returned_value = nyml_get_config(config_key);
		printf("\n\t------->>>>>>  ERROR: %s \"%s\" should return true, instead \"%s\" <<<<<<\n\n",config_key,config_update_value,returned_value);
		CuAssertTrue(tc, 0==2 );
		
	}
	
	printf("\n   should find nothing   ");
	if (returned_value != NULL) free(returned_value);
	returned_value = nyml_get_config(config_key_fake);
	
	if ( strlen(returned_value) != 0){
		printf("\n\t strlen %d - should be should be 0 but gave %s",(int)strlen(returned_value),returned_value);
		printf("\n   should find nothing  inside  ");
		CuAssertTrue(tc, 0==2 );
	}
	 
	printf("\n   should find nothing  end  ");
	 
	
}

void TestCuDBURLs(CuTest* tc)
{
	NYMRL_ID nid = 132612367L;
	int write_loop_count = 11;
	int read_loop_count = 2;
	int i;
	NYMRL_URL_LIST urls;
	
	for (i=0;i<write_loop_count;++i){
		nid =  _nyml_create_node(nid,"my node");
		CuAssertTrue(tc, nid > 0);
		
		_nyml_add_node(nid, "http://localhost12", "child");
		_nyml_add_node(nid, "http://localhost45", "child");
		_nyml_add_node(nid, "http://localhost23", "child");
		_nyml_add_node(nid, "http://localhost4", "peer");
		_nyml_add_node(nid, "http://localhost6", "peer");
		_nyml_remove_node( nid);
		nid = nid-1;

	}
	
	printf("\n\t-> created %d nodes nid=%lu\n",write_loop_count,nid);
	
	int row_count;
	
	for (i=0;i<read_loop_count;++i){
		// urls = _nymrl_get_urls(1, nid, "child",&row_count);
		//CuAssertTrue(tc, strlen(*urls) > 0);
		//printf("\t  %s",*urls);
		//free(*urls);
	}
	
	printf("\n\t-> read %d nodes nid=%lu \n",read_loop_count,nid);
//	NYMRL_URL_LIST result = _nymrl_get_urls(3, nid, "child",&row_count);
	
		
//	urls = _nymrl_get_urls(1, nid, "child",&row_count);
//	if (strlen(urls[0]) != 0){
//		printf("\n\t should be 0 length  %s",urls[0]);
//		CuAssertTrue(tc, FALSE );
//	}
	
	
	
	//NYMRL_RESULT_ITERATOR(result,1);
	//printf("\n got random url %s\n",urls[0] );
	
	
}

CuSuite* CuGetSuiteOne(void)
{
	CuSuite* suite = CuSuiteNew();
    SUITE_ADD_TEST(suite, TestCuConfigValues);
    SUITE_ADD_TEST(suite, TestCuDBURLs);
	return suite;
}

/**
 * tests header messages
 * */
void TestCuHeaderMessages(CuTest* tc){
//	enum nymrl_fwd { NONE, ALL, ANY, ONE, SEND_BEFORE=10,SEND_AFTER=20, RESPOND_BEFORE=100, RESPOND_AFTER=200  };
	enum nymrl_fwd _fwd = ANY;
	char * header_string = (char *) nymrl_new_header(192844L, "rel", 12342, "msid", "rmid", 13941834L, 1, _fwd ,"auth","data=true&jonathan=success","act", "string",FALSE);
	
	nyml_header_struct * header_struct = 
		( nyml_header_struct *) nymrl_new_header(192844L, "rel", 12342, "msid", "rmid", 13941834L, 1,_fwd ,"auth","data=true&jonathan=success","act", NULL,FALSE);
	
	
	nyml_header_struct * header_struct_from_string = nymrl_get_header_from_string(header_string, FALSE);
//	
	char * header_string_from_original_struct = nymrl_get_string_from_header(header_struct, FALSE);
	char * header_string_from_derived_struct = nymrl_get_string_from_header( header_struct_from_string, FALSE);
//	
	if (strcmp(header_string,header_string_from_original_struct) != 0){
	printf("\n +++ header_string_from_original_struct match failed +++ \n%s\n%s\n\n",header_string,header_string_from_original_struct);
		CuAssertTrue(tc, 0==2 );
	}
	
	if (strcmp(header_string,header_string_from_derived_struct) != 0){
	printf("\n+++ header_string_from_derived_struct match failed +++ \n%s\n%s\n\n",header_string,header_string_from_derived_struct);
		CuAssertTrue(tc, 0==2 );
	}
//	 
//	
//	 
//	 
//	  
//	
//	
	free(header_string);
	free(header_string_from_derived_struct);
	free(header_string_from_original_struct);
	free(header_struct_from_string);
	free(header_struct);
	printf("\ncompleted header tests");
}

/**
 * helps for callback
 * t*/
char * myfunction(char * act, char * data){
	printf("\n* callback function worked %s %s\n",act,data);
	return data;
}

void TestCuCallback(CuTest* tc){
   //set the function
   char * returned;
   char * action = (char *)malloc(100* sizeof(char));
   char * data = (char *) malloc(100* sizeof(char));
   sprintf(action,"ACTION");
   sprintf(data,"DATA");
   
   nymrl_callback_function = &myfunction;
   if (nymrl_callback_function != NULL){
	   (*nymrl_callback_function)("INTERNAL TEST","INTERNAL TEST");
   }
   returned = _nymrl_do_callback(action, data);
   printf("\n &&&&&&&&&&&&&&&&& %s",returned);
   CuAssertTrue(tc, (returned && (strlen(returned)>1)) );
   free(action);
   free(data);
}




//char * nymrl_curl_post(char * url, char * post_data, char * cookies)
void TestCuCURL(CuTest* tc){
    char * URL = "http://localhost/admin/curltest.php";
    char * post_values = "jonathan=5&sonia=10";
    char * result = nymrl_curl_post(URL, post_values, NULL);
     
    printf("\n CURL result = %s \n",result);
    if(result) free(result);
}

// 
void TestCuForward(CuTest* tc){
    char * URL = "http://localhost/admin/forwardtest.php";
    int url_count = 5;
    NYMRL_URL_LIST urls = malloc(sizeof(char *) * url_count);
    NYMRL_URL_LIST urlsp = urls;
    nyml_header_struct *  header;
    nyml_header_struct *  recieved;
    int i;
    
    //create list of urls
    for (i=0;i<url_count;++i){
    	*urlsp = URL;
    	urlsp++;
    }
    
    //create a NYMRL header
    char * post_values1 = "post1=90878&adfadfadsf=q340012394";
   	header = ( nyml_header_struct *) nymrl_new_header(192844L, "rel", 12342, "msid", "rmid", 13941834L, 1,210 ,"auth",post_values1,"action", NULL,FALSE);
    
    char * result;
    recieved = _nymrl_do_forward( urls, url_count, header);
    
    result =  nymrl_get_string_from_header(recieved, TRUE);
    printf("\n\n\n\ FORWARD result = %s \n\n\n\n",result);
    if(result) free(result);
}



void TestCuFowardMessages(CuTest* tc){
	 int i1 = 123;
	 int i2 = 40;
     ////	enum nymrl_fwd { NONE, ALL, ANY, ONE, SEND_BEFORE=10,SEND_AFTER=20, RESPOND_BEFORE=100, RESPOND_AFTER=200  };
	 CuAssertTrue(tc, nymrl_first(i1) == 3 );
	 CuAssertTrue(tc, nymrl_second(i1) ==20);
	 CuAssertTrue(tc, nymrl_third(i1) == 100);
	 
	 CuAssertTrue(tc, nymrl_first(i2) == 0 );
	 CuAssertTrue(tc, nymrl_second(i2) ==40);
	 CuAssertTrue(tc, nymrl_third(i2) == 0);
	 
	 enum nymrl_fwd fwd = ANY +SEND_BEFORE +RESPOND_AFTER;
	 printf("\n tested nymrl_first() , fwd=%d, %d,%d,%d\n", fwd,nymrl_first(fwd),nymrl_second(fwd),nymrl_third(fwd));
	 
	 
	 CuAssertTrue(tc, nymrl_first(fwd) == ANY );
	 CuAssertTrue(tc, nymrl_second(fwd) == SEND_BEFORE);
	 CuAssertTrue(tc, nymrl_third(fwd) == RESPOND_AFTER);
}

/**
 * tests header messages encodings
 * */
void TestCuZHeaderMessageEncodings(CuTest* tc){
	 const char * original = "010300745#^$%&*()ASKFHJGASFU$%^&*h===%%%&&&   •†©¥©˚≈øˆ…ƒ˚∂†¥ˆß¥¥†¥©©˙˚øˆ†ˆ•¶∞•¶¶•¶¶§•ªˆ¨¥†ˆ¨¥†®´"
		 //math symbols and control cars
		 "∑øπ“ß∂ƒ©˙∆˚¬¬Ω≈ç√∫˜∫√√çç≈Ω≈≈≈\n\n\t\t   --__<><>gajdsfjasdf"
		 //chinese symbols
		 "新 闻网 页贴 吧知 道MP3图 片 帮助 高级 空间 | 更多";
	 int new_length;
//	 
	 const char * simple_encode = "Hello my name isn't fred. Please stop by. </end>";
	 char * base64enc = nymrl_base64_encode(simple_encode);
     char * base64dec = nymrl_base64_decode(base64enc);
////	 
//	if (strcmp(base64dec,simple_encode) != 0){
//		printf("\n+++ base 64 failed +++ \n\n");
//		CuAssertTrue(tc, 0==2 );
//	} else {
//		printf("\n base64 encode/decode matched!");
//	}
	 
	 printf("\n orig %s\n",original);
	 
	 char * encoded = nymrl_raw_url_encode(original, strlen(original), &new_length);
	 printf("\n encoded %s\n",encoded);
	 
	 nymrl_raw_url_decode(encoded,  strlen(encoded) );
	 
	 printf("\n encoded decoded %s\n",encoded);
	 
	 if (strcmp(encoded,original) != 0){
		printf("\n+++ raw urlencoding failed +++ \n\n");
		CuAssertTrue(tc, 0==2 );
	} else {
		printf("\n rawurl encode/decode matched!");
	}
	
	
	 
	free(encoded);
	//free(base64dec);
	//free(base64enc);
}


/**
 * test compression/encryption
 * test sizes types of user values
 * test retrieval of user string
 * test retrieval of header string
 * */
void TestCuMainMessages(CuTest* tc){
 
 
	
}

/**
 * first  
 * */
CuSuite* CuGetSuiteTwo(void){
	CuSuite* suite = CuSuiteNew();
	SUITE_ADD_TEST(suite, TestCuZHeaderMessageEncodings);
	SUITE_ADD_TEST(suite, TestCuHeaderMessages);
	SUITE_ADD_TEST(suite, TestCuFowardMessages);
	SUITE_ADD_TEST(suite, TestCuMainMessages);
	SUITE_ADD_TEST(suite, TestCuCallback);
	SUITE_ADD_TEST(suite, TestCuCURL);
	SUITE_ADD_TEST(suite, TestCuForward);
	
	
	return suite;
}


#endif /*TEST_H_*/
